Skip to content

feat(geocoder): derive geocoderProvider from Poracle's remote config - #1244

Merged
TurtIeSocks merged 2 commits into
fix/geocoder-photon-misconfigfrom
feat/geocoder-provider-from-poracle
Aug 24, 2026
Merged

feat(geocoder): derive geocoderProvider from Poracle's remote config#1244
TurtIeSocks merged 2 commits into
fix/geocoder-photon-misconfigfrom
feat/geocoder-provider-from-poracle

Conversation

@TurtIeSocks

Copy link
Copy Markdown
Collaborator

Stacked on #1243. Base is fix/geocoder-photon-misconfig so the shared test file does not conflict; retarget to main once that merges.

Requires the PoracleNG side, which adds provider to the /api/config/poracleWeb payload.

The asymmetry this removes

nominatimUrl already falls back to Poracle's providerURL, so a deployment can take its geocoder URL from Poracle and never mention it in ReactMap's config. The backend type had no such fallback:

if (providerURL && !this.nominatimUrl) this.nominatimUrl = providerURL   // derived
// geocoderProvider — local config only

Half the pair auto-populated and the other half did not. An operator whose URL came from Poracle still had to hand-set geocoderProvider locally, and if they did not, every request silently took the Nominatim branch against a Photon URL. That is not a hypothetical: it is what took a production instance down after #1242 shipped.

PoracleNG now reports the backend behind providerURL as provider, so geocoderProvider can follow the same rule as its URL. Local config still wins, matching how providerURL and addressFormat already behave.

Ignored backends

Poracle also offers google and none. Neither has a ReactMap equivalent, so they resolve to undefined rather than being mapped onto nominatim, which would claim something untrue about the backend. The match is exact, so a differently-cased value is ignored rather than guessed at.

The name collision

provider is destructured out of remoteConfig whether or not it is usable:

const { providerURL, provider, addressFormat, ...rest } = remoteConfig
Object.assign(this, rest)

this.provider is the webhook provider, set from webhook.provider in the constructor. It is an unrelated field that happens to share a name with Poracle's geocoding provider, and letting the remote value through the spread would silently overwrite it. It is currently read nowhere else in ReactMap, so this would have been invisible until something started reading it.

Testing

The resolution lives in a small exported function because #fetchConfig is private and would otherwise need a stubbed HTTP round trip to reach.

Four cases: the derivation, local precedence over the remote value, the ignored backends, and the webhook provider surviving a remote payload that carries a geocoding provider.

  • yarn lint passes
  • yarn build passes
  • yarn prettier passes
  • node --test server/test/geocoder.test.js passes 30/30

Not exercised against a live Poracle. The payload shape is taken from PoracleNG's poracleWebResponse struct and its OpenAPI golden, not from a real response, so the field name is worth confirming against a running instance before merge.

yarn test also runs server/test/rocketPokemonFiltering.test.js, which fails with No database selected for React Map Tables. Unrelated and predates this branch: server/src/db/knexfile.cjs calls process.exit(9) at import when no schema has user in its useFor.

@Mygod

Mygod commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

The new fallback can pair Poracle's provider type with a different locally configured URL, regressing valid existing Nominatim configurations when Poracle uses Photon.

Review comment:

  • [P1] Only inherit the provider with its URL — server/src/services/Poracle.js:271-274
    When nominatimUrl is set locally and geocoderProvider is omitted—the existing Nominatim-default configuration—while Poracle reports Photon, the preceding block keeps the local URL but this unconditional fallback installs photon. geocoder() then uses the Photon protocol against the local Nominatim endpoint, causing searches and reverse lookups to return {}. Only derive the remote provider when the remote providerURL was also selected, unless a provider was explicitly configured locally.

@TurtIeSocks

Copy link
Copy Markdown
Collaborator Author

Correct, and this was a regression I introduced rather than a gap. Fixed in 98298a0.

The fallback was unconditional, so a webhook with a local nominatimUrl and no geocoderProvider inherited Photon from a Photon-backed Poracle while keeping its own Nominatim URL. That is the default shape of every existing Nominatim deployment, and it would have broken endpoints that were working fine before this PR.

The backend type is now inherited only when the URL was inherited with it. A URL and the protocol used to talk to it are one setting in two fields, and splitting them across two sources cannot produce a working pair except by luck. An explicit local geocoderProvider still wins in either case, which remains the only way to run a backend Poracle does not report.

resolveGeocoderProvider now takes a named argument object rather than a third positional boolean, so the pairing rule reads at the call site.

Three cases cover it: inheriting when the URL came from Poracle, refusing to inherit when it did not, and local config winning regardless. Confirmed failing without the guard (34/35) rather than assumed.

The irony is not lost on me. This PR exists to fix an asymmetry in how the pair is populated, and it shipped by breaking the pairing in the other direction.

The branch is rebased on the updated #1243, which now also carries the reverse-shape fix from your other review.

🤖 Addressed by Claude Code

@TurtIeSocks
TurtIeSocks force-pushed the feat/geocoder-provider-from-poracle branch from 5085e19 to 98298a0 Compare August 23, 2026 20:23

@Mygod Mygod left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The revised logic keeps the inherited provider paired with Poracle's URL while preserving explicit local configuration. Focused tests, lint, formatting, and the production build passed with no actionable regression found.

@TurtIeSocks
TurtIeSocks force-pushed the feat/geocoder-provider-from-poracle branch 5 times, most recently from a7dd909 to d3f1414 Compare August 24, 2026 17:12
nominatimUrl already falls back to Poracle's providerURL, so a deployment can
get its geocoder URL from Poracle without restating it here. The backend type
had no such fallback, which left half the pair auto-populating and the other
half not: an operator whose URL came from Poracle still had to hand-set
geocoderProvider locally, or every request silently took the Nominatim branch.

PoracleNG now reports the backend behind providerURL as `provider`, so
geocoderProvider follows the same rule as its URL. Local config still wins,
matching how providerURL and addressFormat already behave.

Poracle also offers google and none. Neither has a ReactMap equivalent, so they
resolve to undefined rather than being mapped onto nominatim, which would claim
something untrue about the backend. The match is exact, so a differently-cased
value is ignored rather than guessed at.

`provider` is destructured out of remoteConfig whether or not it is usable.
this.provider is the *webhook* provider, an unrelated field that happens to
share the name, and letting Poracle's value through Object.assign(this, rest)
would silently overwrite it.

The resolution is a small exported function because #fetchConfig is private and
would otherwise need a stubbed HTTP round trip to reach. Four tests cover the
derivation, local precedence, the ignored backends, and the webhook provider
surviving a remote payload that carries a geocoding provider.
The fallback was unconditional, so a webhook with a local nominatimUrl and no
geocoderProvider inherited Photon from a Photon-backed Poracle while keeping its
own Nominatim URL. That is the default shape of every existing Nominatim
deployment, and it made searches and reverse lookups return {} against an
endpoint that had been working.

A URL and the protocol used to talk to it are one setting in two fields, and
splitting them across two sources cannot produce a working pair by accident.
The backend type is now inherited only when the URL was inherited with it.

An explicit local geocoderProvider still wins in either case, which is the only
way to run a backend Poracle does not report.

resolveGeocoderProvider takes a named argument object rather than a third
positional boolean, so the pairing rule is legible at the call site.
@TurtIeSocks
TurtIeSocks force-pushed the feat/geocoder-provider-from-poracle branch from d3f1414 to b647126 Compare August 24, 2026 22:13
@TurtIeSocks
TurtIeSocks merged commit cc849a6 into fix/geocoder-photon-misconfig Aug 24, 2026
2 checks passed
@Mygod
Mygod deleted the feat/geocoder-provider-from-poracle branch August 25, 2026 02:49
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.51.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants